﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Vk.Methods.Common;

namespace Vk.Methods.Database
{
    /// <summary>
    ///     База данных ВК
    /// </summary>
    public class Database
    {
        private readonly Api _api;

        public Database(Api api)
        {
            _api = api;
        }

        /// <summary>
        ///     Возвращает список кафедр университета по указанному факультету.
        /// </summary>
        /// <param name="facultyId">Идентификатор факультета, кафедры которого необходимо получить.</param>
        /// <param name="offset">Отступ, необходимый для получения определенного подмножества кафедр.</param>
        /// <param name="count">
        ///     Количество кафедр которое необходимо получить.
        ///     По умолчанию 100, максимальное значение 10000
        /// </param>
        public async Task<ListCount<IdTitleObject>> GetChairs(int facultyId, int? offset = null, int? count = null)
        {
            var query = new ApiQuery("database.getChairs", true)
                .Add("faculty_id", facultyId)
                .Add("offset", offset)
                .Add("count", count);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("items/*");
                if (nodes != null && nodes.Any())
                    return new ListCount<IdTitleObject>(result.Int("count").Value,
                                                        nodes.Select(x => new IdTitleObject(x)).ToList());
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список городов
        /// </summary>
        /// <param name="countryId">Идентификатор страны, полученный в методе database.getCountries</param>
        /// <param name="regionId">Идентификатор региона, города которого необходимо получить. (параметр не обязателен) </param>
        /// <param name="q">Строка поискового запроса. Например, Санкт</param>
        /// <param name="needAll">true – возвращать все города. false – возвращать только основные города</param>
        /// <param name="offset">Отступ, необходимый для получения определенного подмножества городов</param>
        /// <param name="count">Количество городов, которые необходимо вернуть</param>
        public async Task<ListCount<IdTitleObject>> GetCities(int countryId,
                                                              int? regionId = null,
                                                              string q = null,
                                                              bool? needAll = null,
                                                              int? offset = null,
                                                              int? count = null)
        {
            var query = new ApiQuery("database.getCities", true)
                .Add("country_id", countryId)
                .Add("region_id", regionId)
                .Add("need_all", needAll)
                .Add("q", q)
                .Add("offset", offset)
                .Add("count", count);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("items/*");
                if (nodes != null && nodes.Any())
                    return new ListCount<IdTitleObject>(result.Int("count").Value,
                                                        nodes.Select(x => new IdTitleObject(x)).ToList());
            }
            return null;
        }

        /// <summary>
        ///     Возвращает информацию о городах по их идентификаторам.
        ///     Идентификаторы (id) могут быть получены с помощью методов users.get, places.getById, places.search,
        ///     places.getCheckins.
        /// </summary>
        /// <param name="cityIds">Идентификаторы городов</param>
        public async Task<List<IdTitleObject>> GetCitiesById(IEnumerable<int> cityIds)
        {
            if (cityIds == null)
                throw new ArgumentNullException(nameof(cityIds));

            var query = new ApiQuery("database.getCitiesById", true).Add("city_ids", cityIds);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                if (result != null && result.ChildNodes.Any())
                    return result.ChildNodes.Select(x => new IdTitleObject(x)).ToList();
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список стран
        /// </summary>
        /// <param name="needAll">true — вернуть список всех стран</param>
        /// <param name="code"> Двухбуквенные коды стран в стандарте ISO 3166-1 alpha-2, для которых необходимо выдать информацию</param>
        /// <param name="offset">Отступ, необходимый для выбора определенного подмножества стран</param>
        /// <param name="count">Количество стран, которое необходимо вернуть</param>
        public async Task<ListCount<IdTitleObject>> GetCountries(bool? needAll = null,
                                                                 IEnumerable<string> code = null,
                                                                 int? offset = null,
                                                                 int? count = null)
        {
            var query = new ApiQuery("database.getCountries", true)
                .Add("need_all", needAll)
                .Add("code", code)
                .Add("offset", offset)
                .Add("count", count);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("items/*");
                if (nodes != null && nodes.Any())
                    return new ListCount<IdTitleObject>(result.Int("count").Value,
                                                        nodes.Select(x => new IdTitleObject(x)).ToList());
            }
            return null;
        }

        /// <summary>
        ///     Возвращает информацию о странах по их идентификаторам
        ///     Идентификаторы (id) могут быть получены с помощью методов users.get, places.getById, places.search,
        ///     places.getCheckins
        /// </summary>
        /// <param name="countryIds">Идентификаторы стран</param>
        public async Task<List<IdTitleObject>> GetCountriesById(IEnumerable<int> countryIds)
        {
            if (countryIds == null)
                throw new ArgumentNullException(nameof(countryIds));

            var query = new ApiQuery("database.getCountriesById", true)
                .Add("country_ids", countryIds);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                if (result != null && result.ChildNodes.Any())
                    return result.ChildNodes.Select(x => new IdTitleObject(x)).ToList();
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список факультетов
        /// </summary>
        /// <param name="universityId">Идентификатор университета, факультеты которого необходимо получить</param>
        /// <param name="offset">Отступ, необходимый для получения определенного подмножества факультетов.</param>
        /// <param name="count">Количество факультетов которое необходимо получить</param>
        public async Task<ListCount<IdTitleObject>> GetFaculties(int universityId, int? offset = null, int? count = null)
        {
            var query = new ApiQuery("database.getFaculties", true)
                .Add("university_id", universityId)
                .Add("offset", offset)
                .Add("count", count);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("items/*");
                if (nodes != null && nodes.Any())
                    return new ListCount<IdTitleObject>(result.Int("count").Value,
                                                        nodes.Select(x => new IdTitleObject(x)).ToList());
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список регионов
        /// </summary>
        /// <param name="countryId">Идентификатор страны, полученный в методе database.getCountries</param>
        /// <param name="q">Строка поискового запроса. Например, Лен</param>
        /// <param name="offset">Отступ, необходимый для выбора определенного подмножества регионов</param>
        /// <param name="count">Количество регионов, которое необходимо вернуть</param>
        public async Task<ListCount<IdTitleObject>> GetRegions(int countryId,
                                                               string q = null,
                                                               int? offset = null,
                                                               int? count = null)
        {
            var query = new ApiQuery("database.getRegions", true)
                .Add("country_id", countryId)
                .Add("q", q)
                .Add("offset", offset)
                .Add("count", count);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("items/*");
                if (nodes != null && nodes.Any())
                    return new ListCount<IdTitleObject>(result.Int("count").Value,
                                                        nodes.Select(x => new IdTitleObject(x)).ToList());
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список классов, характерных для школ определенной страны.
        /// </summary>
        /// <param name="countryId">Идентификатор страны, доступные классы в которой необходимо вернуть.</param>
        public async Task<List<IdTitleObject>> GetSchoolClasses(int? countryId = null)
        {
            var query = new ApiQuery("database.getSchoolClasses", true)
                .Add("country_id", countryId);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("list");
                if (nodes != null && nodes.Any())
                    return nodes.Select(x => new IdTitleObject(x, "item[1]", "item[2]")).ToList();
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список школ
        /// </summary>
        /// <param name="countryId">Идентификатор страны, школы которого необходимо вернуть</param>
        /// <param name="cityId">Идентификатор города, школы которого необходимо вернуть</param>
        /// <param name="q">Строка поискового запроса. Например, гимназия</param>
        /// <param name="offset">Отступ, необходимый для получения определенного подмножества школ</param>
        /// <param name="count">Количество школ, которое необходимо вернуть</param>
        public async Task<ListCount<IdTitleObject>> GetSchools(int countryId,
                                                               int cityId,
                                                               string q = null,
                                                               int? offset = null,
                                                               int? count = null)
        {
            var query = new ApiQuery("database.getSchools", true)
                .Add("country_id", countryId)
                .Add("city_id", cityId)
                .Add("q", q)
                .Add("offset", offset)
                .Add("count", count);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("items/*");
                if (nodes != null && nodes.Any())
                    return new ListCount<IdTitleObject>(result.Int("count").Value,
                                                        nodes.Select(x => new IdTitleObject(x)).ToList());
            }
            return null;
        }

        /// <summary>
        ///     Возвращает информацию об улицах по их идентификаторам
        /// </summary>
        /// <param name="streetIds">Идентификаторы улиц</param>
        public async Task<List<IdTitleObject>> GetStreetsById(IEnumerable<int> streetIds)
        {
            var query = new ApiQuery("database.getStreetsById", true)
                .Add("street_ids", streetIds);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                if (result != null && result.ChildNodes.Any())
                    return result.ChildNodes.Select(x => new IdTitleObject(x)).ToList();
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список высших учебных заведений
        /// </summary>
        /// <param name="countryId">Идентификатор страны, учебные заведения которой необходимо вернуть</param>
        /// <param name="cityId">Идентификатор города, учебные заведения которого необходимо вернуть</param>
        /// <param name="q">Строка поискового запроса. Например, СПБ</param>
        /// <param name="offset">Отступ, необходимый для получения определенного подмножества учебных заведений</param>
        /// <param name="count">Количество учебных заведений, которое необходимо вернуть</param>
        public async Task<ListCount<IdTitleObject>> GetUniversities(int countryId,
                                                                    int cityId,
                                                                    string q = null,
                                                                    int? offset = null,
                                                                    int? count = null)
        {
            var query = new ApiQuery("database.getUniversities", true)
                .Add("country_id", countryId)
                .Add("city_id", cityId)
                .Add("q", q)
                .Add("offset", offset)
                .Add("count", count);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("items/*");
                if (nodes != null && nodes.Any())
                    return new ListCount<IdTitleObject>(result.Int("count").Value,
                                                        nodes.Select(x => new IdTitleObject(x)).ToList());
            }
            return null;
        }
    }
}